﻿// Inner Fire 游戏引擎库
// Animator - 动画状态机
//
// Copyright (c) 2025 by 尔西大帝. All rights reserved.
//
// 所属层级：应用层(4)
// 用于管理游戏中的动画状态。
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2025-04-24

#include "animator.h"
#include <game/object/game_object.h>
#include <game/object/instanced_sprite_object.h>
#include <game/physics/physics_bone.h>
#include <klog.h>
#include <knumber.h>

namespace ifire::game {
Animator::Animator(GameObject* game_object)
    : Component(ComponentType(), game_object)
    , armature_(game_object->GetArmature())
    , skin_animator_(game_object->GetSkinAnimator())
    , parents_(armature_->GetNodeTree()->size())
    , final_mats_(armature_->GetBoneCount())
    , use_root_motion_(false)
    , main_clip_(nullptr)
    , physics_bone_(nullptr) {
  // 增加默认的Channel
  channel0_ = AddChannel("Default");
  // 初始化humanoid
  humanoid_ = std::make_unique<Humanoid>(this);
  // 初始化事件管理器
  events_ = std::make_unique<AnimatorEvents>(this);
}

Animator::~Animator() {
  for (auto e : channels_) {
    delete e;
  }
  channels_.clear();
}

void Animator::Play(AnimationClip* clip, PlayMethod loop, float transition) {
  // 更新MainClip
  main_clip_ = clip;
  main_clip_tick_ = 0.0f;
  events_->Clear();

  method_ = AnimatorExecuteMethod::Normal;
  channel0_->Play(Playable(clip, loop), transition);
}

void Animator::PlayBlend(
    AnimationBlend* blend, PlayMethod loop, float transition) {
  // 更新MainClip（这里Blend只能获得第一个，也就是说，同一个Blend里面的动作，应该是一组共用Attach最好）
  main_clip_ = blend->GetRay(0).items[0].GetClip();
  main_clip_tick_ = 0.0f;

  method_ = AnimatorExecuteMethod::Normal;
  channel0_->Play(Playable(blend, loop), transition);
}

void Animator::PlayPose() {
  method_ = AnimatorExecuteMethod::Pose;
  channel0_->Clear();
}

void Animator::PlayAvatar() {
  method_ = AnimatorExecuteMethod::Avatar;
  channel0_->Clear();
}

void Animator::PlayRaw() {
  method_ = AnimatorExecuteMethod::Raw;
  channel0_->Clear();
  // 进行设置为单位矩阵
  for (auto& e : PrimaryList()) {
    e = ID_MAT;
  }
}

void Animator::PlayFrame(AnimationClip* clip, float frame) {
  // 更新MainClip
  main_clip_ = clip;

  Playable playable(clip, PlayMethod::Frame);
  playable.SetFrame(frame);

  channel0_->Play(playable);

  // 执行一次演算。以后在Update中不会再演算了。
  channel0_->Execute(playable);

  // 将最终矩阵保存
  auto& primary_list = PrimaryList();
  for (size_t i = 0; i < final_mats_.size(); i++) {
    final_mats_[i] = primary_list[i];
  }
}

void Animator::SetValue(float x, float y) {
  channel0_->current.value = {x, y};
  channel0_->next.value = {x, y};
}

void Animator::UseRootMotion(bool value) { use_root_motion_ = value; }

bool Animator::IsRootMotion() const { return use_root_motion_; }

DirectX::XMFLOAT3 Animator::GetRootMotionDelta() const {
  DirectX::XMFLOAT3 delta;
  XMStoreFloat3(&delta, channel0_->GetMix().GetDelta());
  return delta;
}

dx::SkinAnimator* Animator::GetSkinAnimator() const { return skin_animator_; }

void Animator::FixedUpdate(float delta_time) {
  this->deltaTime = delta_time;

  if (method_ == AnimatorExecuteMethod::Normal) {
    OnNormal(delta_time);

  } else if (method_ == AnimatorExecuteMethod::Pose) {
    OnPose();
  } else if (method_ == AnimatorExecuteMethod::Avatar) {
    OnAvatar();
  } else if (method_ == AnimatorExecuteMethod::HumanoidFrame) {
    OnHumanoid();
  }

  // 将最终矩阵保存
  auto& primary_list = PrimaryList();
  for (size_t i = 0; i < final_mats_.size(); i++) {
    final_mats_[i] = primary_list[i];
  }
  // 进行物理演算
  if (physics_bone_) {
    physics_bone_->UpdatePhysics(delta_time);
  }

  // 与Offset演算，得到最终的矩阵
  armature_->GetFinal(final_mats_, skin_animator_->mats_);

  // 从动画中提取并刷新
  skin_animator_->SetRefresh();


}

void Animator::LateUpdate(float delta_time) {}

ikit::sstr Animator::ComponentType() {
  // 因为static_string的特殊性，所以这里需要延迟载入。不能直接使用常量。
  // 这很蛋痛，不知道以后有没有好的办法。
  static ikit::sstr TYPE_NAME("Animator");
  return TYPE_NAME;
}

const char* Animator::GetMetaName() { return "AnimatorMeta"; }

AnimatorChannel* Animator::AddChannel(ikit::sstr name) {
  int index = static_cast<int>(channels_.size());
  auto item = new AnimatorChannel(this, name, index);
  channels_.push_back(item);
  return item;
}

FMat Animator::GetBoneFinalMatrix(int bone_id) { return final_mats_[bone_id]; }

FMat4 Animator::GetBoneRenderMatrix(ikit::sstr name) {
  int bone_id = armature_->GetBoneId(name);
  return skin_animator_->mats_[bone_id];
}

FMat4 Animator::GetBoneRenderMatrix(int bone_id) {
  return skin_animator_->mats_[bone_id];
}

Trans3 Animator::GetBoneTransform(ikit::sstr name) {
  auto& mat = GetBoneRenderMatrix(name);
  return Trans3(XMLoadFloat4x4(&mat));
}

void Animator::Print(ikit::sstr name) {
  if (name == "ArmatureTree") {
    GetArmature()->GetNodeTree()->PrintXml(L"output.txt");
  } else if (name == "ArmatureData") {
    GetArmature()->GetNodeTree()->PrintData("output.txt");
  } else if (name == "BoneData") {
    GetArmature()->PrintBoneData("output.txt");
  } else if (name == "Compare") {
    GetArmature()->GetAvatar()->PrintXml(
        GetArmature()->GetNodeTree(), "output.txt");
  }
}

void Animator::OnPose() {
  // 这里直接使用Mix
  GetStaticPose(PrimaryList(), ARMATURE_STATIC_POSE);
}

void Animator::OnAvatar() {
  // 这里直接使用Mix
  GetStaticPose(PrimaryList(), ARMATURE_STATIC_AVATAR);
}

void Animator::OnNormal(float delta_time) {
  // 步进动画
  channel0_->current.AddDeltaTime(delta_time);
  if (!channel0_->name.empty()) {
    channel0_->next.AddDeltaTime(delta_time);
  }

  // 进行播放
  channel0_->Play();

  
  // 对于事件进行处理
  if (main_clip_){
    main_clip_tick_ += deltaTime;
    events_->PickEvent(main_clip_, main_clip_tick_);
  }
}

void Animator::OnHumanoid() {
  // Humanoid思路
  // 1、得到动画的骨架。通过这个骨架计算动画。得到一个动画的Node矩阵anime_transforms。
  // 2、将anime_transforms印射转化为Avatar的矩阵avatar_transform。
  //  - 如何做？是得到这个矩阵数据和T-Pose之间的差值！
  // 3、将avatar_transform印射转化为实际骨骼对象的矩阵。
  //  - 将原来的Avatar的t-pose数据，加上差值！

  // 得到动画的Node
}

void Animator::SetSpriteInformation(
    ikit::sstr type, InstancedSpriteObject* obj) {
  if (!printer_) {
    printer_ = std::make_unique<AnimatorPrinter>(this);
  }
  printer_->SetSpriteInformation(type, obj);
}

void Animator::GetStaticPose(MatrixList& transforms, int type) {
  // 访问优化
  const auto& nodes = armature_->GetNodeTree()->Nodes();

  // 用于保存相对矩阵
  XMMATRIX local{};

  // 因为Nodes数组的顺序，保证Parent的节点在子节点之前先处理。
  for (size_t i = 0; i < nodes.size(); i++) {
    const auto& node = nodes[i];
    // 得到父级矩阵
    const XMMATRIX parent =
        node->parent ? parents_[node->parent->index] : ID_MAT;

    // 通过Bone的数据建立一个变换矩阵
    if (type == ARMATURE_STATIC_POSE) {
      local = node->transformation;
    } else if (type == ARMATURE_STATIC_AVATAR) {
      // 从Avatar中获得项目
      auto& pose_bone = armature_->GetAvatar()->FindPoseBone(node->name);
      local = XMMatrixAffineTransformation(SCALE1, VEC0,
          pose_bone.transform.rotation, pose_bone.transform.position);
    }

    // 创建当前矩阵
    const XMMATRIX global = XMMatrixMultiply(local, parent);

    // 配置到骨骼
    if (node->bone_id != -1) {
      transforms[node->bone_id] = global;
    }

    // 更新父级矩阵
    parents_[i] = global;
  }
}

void Animator::SetPhysicsBone(PhysicsBone* physics_bone) {
  physics_bone_ = physics_bone;
}

void Animator::SetAnimeFrame(Snapshot& snapshot) {
  // 访问优化
  const auto& nodes = armature_->GetNodeTree()->Nodes();

  // 用于保存相对矩阵
  XMMATRIX local{};

  // 因为Nodes数组的顺序，保证Parent的节点在子节点之前先处理。
  for (size_t i = 0; i < nodes.size(); i++) {
    const auto node = nodes[i];
    // 获得当前的节点
    auto node_mat = node->transformation;

    // 检查是否有动画。通过是否拥有Avatar，进行不同的处理（失败）
    auto channel = snapshot.clip->FindChannel(node->name);

    // 如果有动画，这个动画的位置就会替换Trans的变换
    bool has_anime = false;
    if (channel) {
      node_mat = snapshot.clip->CalcTransform(snapshot.frame, channel);
      has_anime = true;
    }

    // 如果有些动画需要进行翻转，在Root的节点进行翻转。
    if (node->avatar_id == AVATAR_ROOT_ID && snapshot.clip->IsFlipRotation()) {
      node_mat = node_mat * snapshot.clip->GetFlipRotation();
    }

    // 得到父级矩阵
    const XMMATRIX parent =
        node->parent ? parents_[node->parent->index] : ID_MAT;
    // 获得当前这个节点的变换
    auto global = XMMatrixMultiply(node_mat, parent);

    // 检查Root
    if (snapshot.clip->IsRootMotion() && use_root_motion_) {
      if (node->IsRootMotionNode()) {
        snapshot.ExecuteRootMotion(global);
      }
    } else {
      snapshot.PassRootMotion();
    }

    // 配置到骨骼
    if (node->bone_id != -1) {
      snapshot.transforms[node->bone_id] = global;
    }

    // 更新父级矩阵
    parents_[i] = global;
  }
}

MatrixList& Animator::PrimaryList() { return channel0_->GetMix().transforms; }

} // namespace ifire::game
